<script lang="ts" setup>
import SparkMD5 from "spark-md5"
import {computed, onMounted, ref, watch} from "vue";
import FileApi from "@/api/file/FileApi.ts";
import {UploadRawFile, UploadRequestOptions, UploadUserFile} from "element-plus";
import {FileBo, FileSearchBo, FileVo} from "@/dto/file/File.ts";
import {UploadFilled} from "@element-plus/icons-vue";
import { ListUtil} from "@/util/ListUtil.ts";

const props = defineProps({
    type: 'file' | "image",
    modelValue: String,
    disabled:{
        type:Boolean,
        default:true
    }
})

//分片大小为5MB
const CHUNK_SIZE = 2 * 1024 * 1024
const fileApi = ref<FileApi>(new FileApi())
const fileList = ref<Array<UploadUserFile>>([])
const emit = defineEmits(["update:modelValue"])

onMounted(() => {
    changFileList(value.value)
})

const value = computed({
    get() {
        return props.modelValue;
    },
    set(value) {
        emit("update:modelValue", value)
    }
})

async function changFileList(value) {
    if (!value) {
        fileList.value = []
        return
    }
    const idList = ListUtil.stringToList(value)
    let searchBo = new FileSearchBo()
    searchBo.idList = idList
    const result = await fileApi.value.listAll(searchBo)
    const data = result.data
    fileList.value = data?.map(vo => FileVo.toUploadFile(vo) ?? [])
}

/**
 * @description: 文件加密处理
 */
const getMD5 = (file: any): Promise<string> => new Promise((resolve, reject) => {
    const spark = new SparkMD5.ArrayBuffer();
    // 获取文件二进制数据
    const fileReader = new FileReader();
    fileReader.readAsArrayBuffer(file); // file 就是获取到的文件
    // 异步执行函数
    fileReader.addEventListener('load', (e: any) => {
        spark.append(e.target.result);
        const md5: string = spark.end();
        resolve(md5);
    });
    fileReader.addEventListener('error', (e) => {
        reject(e);
    });
});


/**
 * 分片上传
 * @param option
 */
async function uploadFile(option: UploadRequestOptions): any {
    let file = option.file
    let md5 = await getMD5(file)
    let count = file.size / CHUNK_SIZE
    let fileBo = new FileBo(file.name, file.type, file.name.split(".")[1], md5, count)
    for (let i = 0; i < count; i++) {
        let start = i * CHUNK_SIZE
        let end = Math.min(start + CHUNK_SIZE, file.size)
        fileBo.number = i;
        let form = new FormData()
        form.append("file", file.slice(start, end))
        form.append("fileBo", new Blob([JSON.stringify(fileBo)], {type: 'application/json'}))
        let result = await fileApi.value.upload(form);
        if (result.data?.finish) {
            return result.data.id
        }
        if (result.data?.number != i) {
            i = result.data?.number as number
        }
    }
}

watch(value, (v) => {

})

watch(fileList, (newValue) => {
    value.value = ListUtil.listToString(newValue.map(file => file.response).filter(id => id))
}, {
    deep: true
})

</script>
<template>
    <div v-if="type == 'file'">
        <el-upload
                v-model:file-list="fileList"
                action="/"
                :multiple="true"
                :http-request="uploadFile"
        >
            <el-button
                    type="primary"
            >
                上传文件
            </el-button>
        </el-upload>
    </div>
    <div v-if="type == 'image'">
        <el-upload
                action="/"
                v-model:file-list="fileList"
                list-type="picture-card"
                :multiple="true"
                :http-request="uploadFile"
                class="avatar-uploader">
            <el-icon class="avatar-uploader-icon">
                <upload-filled/>
            </el-icon>
        </el-upload>
    </div>
</template>

<style scoped>
.avatar-uploader .el-upload {
    border: 1px dashed var(--el-border-color);
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    transition: var(--el-transition-duration-fast);
}

.avatar-uploader .el-upload:hover {
    border-color: var(--el-color-primary);
}

.el-icon.avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 178px;
    height: 178px;
    text-align: center;
}
</style>